package vip.hello520.config;

import com.alibaba.dubbo.common.utils.StringUtils;
import org.apache.commons.codec.DecoderException;
import org.apache.curator.framework.CuratorFramework;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
import org.springframework.core.io.ClassPathResource;
import vip.hello520.util.ZkUtils;

import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.util.List;
import java.util.Properties;

/**
 * 加载zk统一配置数据
 *
 * @author 大宝杰
 */
public class ZkPropConfigDown extends PropertyPlaceholderConfigurer {
    private static Logger logger = LoggerFactory.getLogger(ZkPropConfigDown.class);

    /**
     * zk服务器地址
     */
    private String zkUrl;
    /**
     * zk中,存储配置文件的节点的父节点
     */
    private String zkNameSpace;
    /**
     * zk中,存储配置文件的节点名称
     */
    private List<String> propConfigNodeName;

    /**
     * 为了增强通用性,zkUrl做成可配置的
     */
    private void initUrl() {
        Properties prop = new Properties();
        ClassPathResource resource = new ClassPathResource("zk.properties");
        try (InputStream inputStream = resource.getInputStream()) {
            prop.load(inputStream);
            this.zkUrl = prop.getProperty("zkUrl");
            this.zkNameSpace = prop.getProperty("zkNameSpace");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * spring框架处理配置参数的方法
     *
     * @param beanFactory
     */
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        try {
            initUrl();
            Properties properties = new Properties();
            for (String nodeName : propConfigNodeName) {
                byte[] zkDataByNodeName = getZkDataByNodeName(nodeName);
                zkDataToProperties(properties, zkDataByNodeName);
            }
            //spring框架的方法,spring自动填充进 配置文件里的${jdbc.url} 或 java类里面的@Value("${admin.h5}")
            super.processProperties(beanFactory, properties);
            logger.info("获取zk配置数据成功！,配置参数:{}", properties.toString());
        } catch (Exception e) {
            logger.error("出错了:", e);
        }
    }

    /**
     * 根据zk节点名称,获取此节点的数据
     *
     * @param nodeName
     * @return
     * @throws Exception
     */
    private byte[] getZkDataByNodeName(String nodeName) {
        byte[] bytes = null;
        try (CuratorFramework zkClient = ZkUtils.getZkClient(zkUrl, zkNameSpace);) {
            if (!ZkUtils.exists(zkClient, nodeName)) {
                throw new RuntimeException("节点 : " + nodeName + "不存在");
            }
            bytes = zkClient.getData().forPath(nodeName);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return bytes;
    }

    /**
     * 将zk取到的byte类型的数据转换成Properties类型
     *
     * @param props
     * @param data
     * @throws UnsupportedEncodingException
     * @throws DecoderException
     */
    private void zkDataToProperties(Properties props, byte[] data) throws UnsupportedEncodingException, DecoderException {
        String dataStr = new String(data, "UTF-8");
        dataStr = dataStr.substring(1, dataStr.length() - 1);
        if (StringUtils.isNotEmpty(dataStr)) {
            if (dataStr.indexOf(",") > 0) {
                String[] values = dataStr.split(",");
                for (String entity : values) {
                    String k = entity.substring(0, entity.indexOf("=")).trim();
                    String v = entity.substring(entity.indexOf("=") + 1).trim();
                    props.put(k, ZkUtils.decode(v));
                }
            } else {
                String k = dataStr.substring(0, dataStr.indexOf("=")).trim();
                String v = dataStr.substring(dataStr.indexOf("=") + 1).trim();
                props.put(k, ZkUtils.decode(v));
            }
        }
    }

    public void setPropConfigNodeName(List<String> propConfigNodeName) {
        this.propConfigNodeName = propConfigNodeName;
    }
}
